package me.zhengjie.modules.hanzi.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.hanzi.exception.ImageProcessingException;
import me.zhengjie.modules.hanzi.service.HanziScoreService;
import me.zhengjie.modules.hanzi.service.HanziService;
import me.zhengjie.modules.hanzi.utils.HanziScoreConfigProperties;
import me.zhengjie.modules.hanzi.utils.ImageProcessingUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 汉字评分服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class HanziScoreServiceImpl implements HanziScoreService {

    private final HanziScoreConfigProperties configProperties;

    private final HanziService hanziService;

    /**
     * 对用户手写汉字进行评分
     * @param standardChar 标准汉字
     * @param userImage 用户手写汉字图片
     * @return 评分结果，包含总分和各维度分数
     */
    @Override
    public Map<String, Object> scoreHandwriting(String standardChar, MultipartFile userImage) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 1. 读取图像
            String characterImg = hanziService.getCharacterImg(standardChar);
            BufferedImage stdImg = ImageProcessingUtils.urlToBufferedImage(characterImg);
            BufferedImage userImg = ImageIO.read(userImage.getInputStream());
            
            // 确保两个图像尺寸一致
            int imageSize = configProperties.getImageSize();
            stdImg = ImageProcessingUtils.resizeImage(stdImg, imageSize, imageSize);
            userImg = ImageProcessingUtils.resizeImage(userImg, imageSize, imageSize);
            
            // 图像预处理
            stdImg = preprocessImage(stdImg);
            userImg = preprocessImage(userImg);
            
            // 2. 特征提取与评分
            // 2.1 形状相似度评分
            double shapeScore = calculateShapeSimilarity(stdImg, userImg);
            
            // 2.2 笔画质量评分
            double strokeQualityScore = calculateStrokeQuality(userImg);
            
            // 2.3 结构评分
            double structureScore = calculateStructure(stdImg, userImg);
            
            // 2.4 整体美观度评分
            double aestheticScore = calculateAesthetic(userImg);
            
            // 3. 计算总分（使用配置的权重）
            double totalScore = shapeScore * configProperties.getWeights().getShape() + 
                               strokeQualityScore * configProperties.getWeights().getStrokeQuality() + 
                               structureScore * configProperties.getWeights().getStructure() + 
                               aestheticScore * configProperties.getWeights().getAesthetic();
            
            // 4. 返回评分结果
            result.put("totalScore", Math.round(totalScore));
            result.put("shapeScore", Math.round(shapeScore));
            result.put("strokeQualityScore", Math.round(strokeQualityScore));
            result.put("structureScore", Math.round(structureScore));
            result.put("aestheticScore", Math.round(aestheticScore));
            result.put("feedback", generateFeedback(shapeScore, strokeQualityScore, structureScore, aestheticScore));
            
        } catch (IOException e) {
            log.error("处理图像时发生错误", e);
            throw new ImageProcessingException("图像读取失败", e);
        } catch (Exception e) {
            log.error("评分过程中发生错误", e);
            throw new ImageProcessingException("评分过程中发生错误", e);
        }
        return result;
    }
    
    /**
     * 图像预处理
     * 1. 调整大小
     * 2. 转为灰度图
     * 3. 二值化
     * 4. 去噪
     */
    private BufferedImage preprocessImage(BufferedImage originalImage) {
        if (originalImage == null) {
            throw new ImageProcessingException("无法读取图像");
        }
        
        try {
            // 1. 调整图像大小为配置的标准尺寸
            int imageSize = configProperties.getImageSize();
            BufferedImage resizedImage = ImageProcessingUtils.resizeImage(originalImage, imageSize, imageSize);
            
            // 2. 转换为灰度图
            BufferedImage grayImage = ImageProcessingUtils.toGrayscale(resizedImage);
            
            // 3. 二值化处理
            BufferedImage binaryImage = ImageProcessingUtils.binarizeWithOtsu(grayImage);
            
            // 4. 应用中值滤波去噪
            return ImageProcessingUtils.applyMedianFilter(binaryImage, 1);
        } catch (Exception e) {
            log.error("图像预处理失败", e);
            throw new ImageProcessingException("图像预处理失败", e);
        }
    }
    
    /**
     * 计算形状相似度
     * 使用模板匹配方法计算两个图像的相似度
     */
    private double calculateShapeSimilarity(BufferedImage stdImg, BufferedImage userImg) {
        try {
            // 检测是否是简单汉字（如"一"）
            boolean isSimpleChar = isSimpleCharacter(stdImg);
            
            // 使用像素匹配计算相似度
            double pixelSimilarity = ImageProcessingUtils.calculateSimilarity(stdImg, userImg) * 100;
            
            // 计算笔画中心线的位置相似度
            double centerLineSimilarity = calculateCenterLineSimilarity(stdImg, userImg);
            
            // 计算黑色像素(墨迹)的数量比例
            int stdBlackPixels = countBlackPixels(stdImg);
            int userBlackPixels = countBlackPixels(userImg);
            
            // 计算黑色像素比例的相似度
            double blackPixelRatio = Math.min(stdBlackPixels, userBlackPixels) / (double) Math.max(stdBlackPixels, userBlackPixels);
            double blackPixelScore = blackPixelRatio * 100;
            
            // 对于简单字形，给予像素匹配更高的基础分
            if (isSimpleChar) {
                // 对简单汉字更宽松的评分
                pixelSimilarity = Math.max(pixelSimilarity, 60);  // 确保基础分不低于60
                return (pixelSimilarity * 0.4 + centerLineSimilarity * 0.4 + blackPixelScore * 0.2);
            } else {
                // 复杂汉字的评分保持不变
                return (pixelSimilarity * 0.4 + centerLineSimilarity * 0.3 + blackPixelScore * 0.3);
            }
            
        } catch (Exception e) {
            log.error("计算形状相似度失败", e);
            throw new ImageProcessingException("计算形状相似度失败", e);
        }
    }
    
    /**
     * 计算黑色像素数量
     */
    private int countBlackPixels(BufferedImage img) {
        int count = 0;
        int width = img.getWidth();
        int height = img.getHeight();
        
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if ((img.getRGB(x, y) & 0xFFFFFF) == 0) { // 黑色像素
                    count++;
                }
            }
        }
        
        return count;
    }
    
    /**
     * 判断是否是简单汉字
     */
    private boolean isSimpleCharacter(BufferedImage img) {
        int width = img.getWidth();
        int height = img.getHeight();
        int blackPixels = countBlackPixels(img);
        
        // 计算黑色像素占比
        double blackRatio = (double) blackPixels / (width * height);
        
        // 检测是否主要在水平或垂直方向上有笔画
        boolean hasHorizontalStroke = hasHorizontalStroke(img);
        boolean hasVerticalStroke = hasVerticalStroke(img);
        
        // 如果黑色像素少且主要在某一个方向上有笔画，则视为简单汉字
        return blackRatio < 0.2 && (hasHorizontalStroke || hasVerticalStroke);
    }
    
    /**
     * 检测是否有水平笔画
     */
    private boolean hasHorizontalStroke(BufferedImage img) {
        int width = img.getWidth();
        int height = img.getHeight();
        int midY = height / 2;
        
        // 统计水平中线附近的黑色像素
        int count = 0;
        for (int x = 0; x < width; x++) {
            // 检查中线上下5%范围内的像素
            int range = Math.max(1, (int)(height * 0.05));
            for (int y = Math.max(0, midY - range); y < Math.min(height, midY + range); y++) {
                if ((img.getRGB(x, y) & 0xFFFFFF) == 0) {
                    count++;
                }
            }
        }
        
        // 如果中线附近有足够多的黑色像素，则认为有水平笔画
        return count > width * 0.3;
    }
    
    /**
     * 检测是否有垂直笔画
     */
    private boolean hasVerticalStroke(BufferedImage img) {
        int width = img.getWidth();
        int height = img.getHeight();
        int midX = width / 2;
        
        // 统计垂直中线附近的黑色像素
        int count = 0;
        for (int y = 0; y < height; y++) {
            // 检查中线左右5%范围内的像素
            int range = Math.max(1, (int)(width * 0.05));
            for (int x = Math.max(0, midX - range); x < Math.min(width, midX + range); x++) {
                if ((img.getRGB(x, y) & 0xFFFFFF) == 0) {
                    count++;
                }
            }
        }
        
        // 如果中线附近有足够多的黑色像素，则认为有垂直笔画
        return count > height * 0.3;
    }
    
    /**
     * 计算中心线相似度
     */
    private double calculateCenterLineSimilarity(BufferedImage stdImg, BufferedImage userImg) {
        // 检测是否有水平或垂直中心线
        boolean stdHorizontal = hasHorizontalStroke(stdImg);
        boolean userHorizontal = hasHorizontalStroke(userImg);
        boolean stdVertical = hasVerticalStroke(stdImg);
        boolean userVertical = hasVerticalStroke(userImg);
        
        // 如果两者在水平或垂直特征上一致，则提高相似度分数
        if ((stdHorizontal && userHorizontal) || (stdVertical && userVertical)) {
            // 计算中心线位置
            double[] stdCenter = calculateStrokeCenterPosition(stdImg);
            double[] userCenter = calculateStrokeCenterPosition(userImg);
            
            // 归一化中心差异
            double xDiff = Math.abs(stdCenter[0] - userCenter[0]) / stdImg.getWidth();
            double yDiff = Math.abs(stdCenter[1] - userCenter[1]) / stdImg.getHeight();
            
            // 中心线位置相似度
            double positionSimilarity = 100 - (xDiff + yDiff) * 50 * 100;
            return Math.max(60, positionSimilarity); // 最低给60分的基础分
        }
        
        // 如果中心线特征不匹配，给一个较低的分数
        return 40.0;
    }
    
    /**
     * 计算笔画中心位置
     */
    private double[] calculateStrokeCenterPosition(BufferedImage img) {
        int width = img.getWidth();
        int height = img.getHeight();
        int totalX = 0;
        int totalY = 0;
        int count = 0;
        
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if ((img.getRGB(x, y) & 0xFFFFFF) == 0) { // 黑色像素
                    totalX += x;
                    totalY += y;
                    count++;
                }
            }
        }
        
        if (count == 0) {
            return new double[]{width / 2.0, height / 2.0};
        }
        
        return new double[]{totalX / (double)count, totalY / (double)count};
    }
    
    /**
     * 计算笔画质量
     * 分析笔画的连贯性、流畅度等
     */
    private double calculateStrokeQuality(BufferedImage userImg) {
        try {
            // 提取轮廓用于分析笔画质量
            BufferedImage contourImg = ImageProcessingUtils.extractContour(userImg);
            
            // 分析笔画边缘的平滑度
            double smoothness = calculateSmoothness(contourImg);
            
            // 分析连通性
            double continuity = calculateContinuity(userImg);
            
            // 综合评分
            return (smoothness * 0.6 + continuity * 0.4);
        } catch (Exception e) {
            log.error("计算笔画质量失败", e);
            throw new ImageProcessingException("计算笔画质量失败", e);
        }
    }
    
    /**
     * 计算笔画平滑度
     */
    private double calculateSmoothness(BufferedImage contourImg) {
        // 在实际应用中，可以使用边缘检测和曲率分析来计算平滑度
        // 这里使用一个简化的方法，基于轮廓像素的分布
        
        // 计算轮廓像素数量
        int width = contourImg.getWidth();
        int height = contourImg.getHeight();
        int contourPixels = 0;
        
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if ((contourImg.getRGB(x, y) & 0xFFFFFF) == 0) { // 黑色像素
                    contourPixels++;
                }
            }
        }
        
        // 计算轮廓密度（轮廓像素数量与图像大小的比例）
        double contourDensity = (double) contourPixels / (width * height);
        
        // 基于轮廓密度计算平滑度分数
        // 假设理想的轮廓密度在0.1到0.2之间
        double idealDensityMin = 0.1;
        double idealDensityMax = 0.2;
        
        if (contourDensity < idealDensityMin) {
            // 轮廓太少，可能是笔画太粗糙
            return 70.0 + (contourDensity / idealDensityMin) * 20.0;
        } else if (contourDensity > idealDensityMax) {
            // 轮廓太多，可能是笔画太复杂或不平滑
            return 90.0 - ((contourDensity - idealDensityMax) / idealDensityMax) * 30.0;
        } else {
            // 轮廓密度在理想范围内
            return 90.0 + ((idealDensityMax - contourDensity) / (idealDensityMax - idealDensityMin)) * 10.0;
        }
    }
    
    /**
     * 计算笔画连贯性
     */
    private double calculateContinuity(BufferedImage img) {
        // 在实际应用中，需要更复杂的连通性分析
        // 这里使用一个简化的方法，基于黑色像素的连通区域数量
        
        // 计算黑色像素总数
        int width = img.getWidth();
        int height = img.getHeight();
        int blackPixels = 0;
        
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if ((img.getRGB(x, y) & 0xFFFFFF) == 0) { // 黑色像素
                    blackPixels++;
                }
            }
        }
        
        // 假设理想情况下，连通区域数量应该很少（汉字笔画应该是连贯的）
        // 这里返回一个基于黑色像素数量的模拟分数
        if (blackPixels < width * height * 0.1) {
            // 黑色像素太少，可能是笔画不完整
            return 60.0 + (blackPixels / (width * height * 0.1)) * 20.0;
        } else if (blackPixels > width * height * 0.5) {
            // 黑色像素太多，可能是笔画太粗或有噪声
            return 80.0 - ((blackPixels - width * height * 0.5) / (width * height * 0.5)) * 20.0;
        } else {
            // 黑色像素数量在合理范围内
            return 80.0 + ((blackPixels - width * height * 0.1) / (width * height * 0.4)) * 20.0;
        }
    }
    
    /**
     * 计算结构评分
     * 分析部件的相对位置和比例
     */
    private double calculateStructure(BufferedImage stdImg, BufferedImage userImg) {
        // 检测是否是简单汉字
        boolean isSimple = isSimpleCharacter(stdImg);
        
        if (isSimple) {
            // 对于简单汉字，使用特殊的结构评分逻辑
            return calculateSimpleCharStructure(stdImg, userImg);
        }
        
        // 原有的结构评分逻辑
        // 对图像进行2x2分区
        int regionsX = 2; 
        int regionsY = 2;
        int width = stdImg.getWidth();
        int height = stdImg.getHeight();
        int regionWidth = width / regionsX;
        int regionHeight = height / regionsY;
        
        double totalScore = 0;
        double[] regionWeights = {0.25, 0.25, 0.25, 0.25}; // 四个区域权重相等
        int regionIndex = 0;
        
        for (int rx = 0; rx < regionsX; rx++) {
            for (int ry = 0; ry < regionsY; ry++) {
                int startX = rx * regionWidth;
                int startY = ry * regionHeight;
                int endX = Math.min(startX + regionWidth, width);
                int endY = Math.min(startY + regionHeight, height);
                
                // 计算该区域的像素分布相似度
                int stdBlackCount = 0;
                int userBlackCount = 0;
                
                for (int x = startX; x < endX; x++) {
                    for (int y = startY; y < endY; y++) {
                        if ((stdImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                            stdBlackCount++;
                        }
                        if ((userImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                            userBlackCount++;
                        }
                    }
                }
                
                // 计算区域内黑色像素比例的相似度
                int totalPixels = (endX - startX) * (endY - startY);
                double stdRatio = (double) stdBlackCount / totalPixels;
                double userRatio = (double) userBlackCount / totalPixels;
                
                // 比较比例差异
                double regionScore;
                if (stdRatio == 0 && userRatio == 0) {
                    regionScore = 100; // 两者都没有黑色像素，视为完全匹配
                } else {
                    double ratioDiff = Math.abs(stdRatio - userRatio);
                    regionScore = Math.max(0, 100 - (ratioDiff * 200)); // 乘200使差异更明显
                }
                
                totalScore += regionScore * regionWeights[regionIndex++];
            }
        }
        
        return totalScore;
    }
    
    /**
     * 计算简单汉字的结构评分
     */
    private double calculateSimpleCharStructure(BufferedImage stdImg, BufferedImage userImg) {
        // 确定是水平还是垂直笔画
        boolean isHorizontal = hasHorizontalStroke(stdImg);
        boolean isVertical = hasVerticalStroke(stdImg);
        
        if (isHorizontal) {
            // 水平笔画的结构评分 - 检查写在正确的高度位置
            return evaluateHorizontalStructure(stdImg, userImg);
        } else if (isVertical) {
            // 垂直笔画的结构评分 - 检查写在正确的水平位置
            return evaluateVerticalStructure(stdImg, userImg);
        } else {
            // 如果不是明确的水平或垂直笔画，使用一般评分
            return 70.0; // 给一个基础分
        }
    }
    
    /**
     * 评估水平笔画的结构
     */
    private double evaluateHorizontalStructure(BufferedImage stdImg, BufferedImage userImg) {
        int height = stdImg.getHeight();
        
        // 计算标准图像中笔画的垂直位置
        int[] stdYPositions = new int[height];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < stdImg.getWidth(); x++) {
                if ((stdImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                    stdYPositions[y]++;
                }
            }
        }
        
        // 找出标准图像的主要笔画位置
        int stdPeakY = 0;
        int maxCount = 0;
        for (int y = 0; y < height; y++) {
            if (stdYPositions[y] > maxCount) {
                maxCount = stdYPositions[y];
                stdPeakY = y;
            }
        }
        
        // 计算用户图像中笔画的垂直位置
        int[] userYPositions = new int[height];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < userImg.getWidth(); x++) {
                if ((userImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                    userYPositions[y]++;
                }
            }
        }
        
        // 找出用户图像的主要笔画位置
        int userPeakY = 0;
        maxCount = 0;
        for (int y = 0; y < height; y++) {
            if (userYPositions[y] > maxCount) {
                maxCount = userYPositions[y];
                userPeakY = y;
            }
        }
        
        // 计算位置差异并评分
        double positionDiff = Math.abs(stdPeakY - userPeakY) / (double)height;
        double structureScore = 100 - (positionDiff * 200); // 位置差异越大，分数越低
        
        // 确保最低分数
        return Math.max(60, structureScore);
    }
    
    /**
     * 评估垂直笔画的结构
     */
    private double evaluateVerticalStructure(BufferedImage stdImg, BufferedImage userImg) {
        int width = stdImg.getWidth();
        
        // 计算标准图像中笔画的水平位置
        int[] stdXPositions = new int[width];
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < stdImg.getHeight(); y++) {
                if ((stdImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                    stdXPositions[x]++;
                }
            }
        }
        
        // 找出标准图像的主要笔画位置
        int stdPeakX = 0;
        int maxCount = 0;
        for (int x = 0; x < width; x++) {
            if (stdXPositions[x] > maxCount) {
                maxCount = stdXPositions[x];
                stdPeakX = x;
            }
        }
        
        // 计算用户图像中笔画的水平位置
        int[] userXPositions = new int[width];
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < userImg.getHeight(); y++) {
                if ((userImg.getRGB(x, y) & 0xFFFFFF) == 0) {
                    userXPositions[x]++;
                }
            }
        }
        
        // 找出用户图像的主要笔画位置
        int userPeakX = 0;
        maxCount = 0;
        for (int x = 0; x < width; x++) {
            if (userXPositions[x] > maxCount) {
                maxCount = userXPositions[x];
                userPeakX = x;
            }
        }
        
        // 计算位置差异并评分
        double positionDiff = Math.abs(stdPeakX - userPeakX) / (double)width;
        double structureScore = 100 - (positionDiff * 200); // 位置差异越大，分数越低
        
        // 确保最低分数
        return Math.max(60, structureScore);
    }
    
    /**
     * 计算整体美观度
     */
    private double calculateAesthetic(BufferedImage userImg) {
        // 分析汉字的整体平衡性和美观度
        // 基于图像的对称性和平衡性
        
        // 计算对称性
        double symmetry = ImageProcessingUtils.calculateSymmetry(userImg) * 100;
        
        // 计算平衡性
        double[] centerOfMass = ImageProcessingUtils.calculateCenterOfMass(userImg);
        double balance = calculateBalance(userImg, centerOfMass);
        
        // 综合评分
        return (symmetry * 0.5 + balance * 0.5);
    }
    
    /**
     * 计算平衡性
     */
    private double calculateBalance(BufferedImage img, double[] centerOfMass) {
        int width = img.getWidth();
        int height = img.getHeight();
        
        // 计算理想重心与实际重心的距离
        double idealX = width / 2.0;
        double idealY = height / 2.0;
        double distance = Math.sqrt(Math.pow(centerOfMass[0] - idealX, 2) + Math.pow(centerOfMass[1] - idealY, 2));
        
        // 归一化距离，转换为分数
        double maxDistance = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) / 2;
        double balanceScore = (1 - distance / maxDistance) * 100;
        
        return Math.max(0, balanceScore);
    }
    
    /**
     * 生成反馈信息
     */
    private String generateFeedback(double shapeScore, double strokeQualityScore, 
                                   double structureScore, double aestheticScore) {
        StringBuilder feedback = new StringBuilder();
        
        // 形状相似度反馈 - 调整判断标准
        if (shapeScore < 40) {
            feedback.append("形状与标准字差异较大，需要多加练习。");
        } else if (shapeScore < 70) {
            feedback.append("形状基本接近标准字，但仍有提升空间。");
        } else {
            feedback.append("形状与标准字非常接近，继续保持。");
        }
        
        // 笔画质量反馈
        if (strokeQualityScore < 60) {
            feedback.append("笔画质量需要提高，注意笔画的流畅度和连贯性。");
        } else if (strokeQualityScore < 80) {
            feedback.append("笔画质量良好，可以进一步提高笔画的精确度。");
        } else {
            feedback.append("笔画质量优秀，书写流畅自然。");
        }
        
        // 结构反馈
        if (structureScore < 60) {
            feedback.append("字的结构需要调整，注意部件间的比例和位置。");
        } else if (structureScore < 80) {
            feedback.append("字的结构基本合理，可以进一步优化部件间的平衡。");
        } else {
            feedback.append("字的结构布局合理，部件比例协调。");
        }
        
        // 美观度反馈
        if (aestheticScore < 60) {
            feedback.append("整体美观度有待提高，注意字的平衡性和协调性。");
        } else if (aestheticScore < 80) {
            feedback.append("整体美观度良好，可以进一步提高字的艺术性。");
        } else {
            feedback.append("整体美观度高，字形优美协调。");
        }
        
        return feedback.toString();
    }
} 